import me.keepz.keepzwalletecommerceservice.model.param.EncryptedResponse;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.OAEPParameterSpec;
import javax.crypto.spec.PSource;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.MGF1ParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
@Slf4j
public class EncryptionUtils {
private static final String CIPHER = "AES/CBC/PKCS5Padding";
public static String encryptUsingPublicKey(String data, String key, Boolean usePkcsPadding) {
byte[] publicKeyBytes = Base64.getDecoder().decode(key);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes);
byte[] encryptedBytes;
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(keySpec);
String transformation = usePkcsPadding == null || usePkcsPadding
? "RSA/ECB/PKCS1Padding"
: "RSA/ECB/OAEPWithSHA-256AndMGF1Padding";
Cipher cipher = Cipher.getInstance(transformation);
if (usePkcsPadding == null || usePkcsPadding) {
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
} else {
OAEPParameterSpec oaepParams = new OAEPParameterSpec(
"SHA-256",
"MGF1",
new MGF1ParameterSpec("SHA-256"),
PSource.PSpecified.DEFAULT
);
cipher.init(Cipher.ENCRYPT_MODE, publicKey, oaepParams);
}
encryptedBytes = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
} catch (NoSuchPaddingException | IllegalBlockSizeException | InvalidKeySpecException |
BadPaddingException | InvalidKeyException | NoSuchAlgorithmException |
InvalidAlgorithmParameterException e) {
throw new RuntimeException(e);
}
return Base64.getEncoder().encodeToString(encryptedBytes);
}
public static String decryptWithPrivateKey(String encryptedData, String key, Boolean usePkcsPadding) {
byte[] privateKeyBytes = Base64.getDecoder().decode(key);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
byte[] decryptedBytes;
try {
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
String transformation = usePkcsPadding == null || usePkcsPadding
? "RSA/ECB/PKCS1Padding"
: "RSA/ECB/OAEPWithSHA-256AndMGF1Padding";
Cipher cipher = Cipher.getInstance(transformation);
if (usePkcsPadding == null || usePkcsPadding) {
cipher.init(Cipher.DECRYPT_MODE, privateKey);
} else {
OAEPParameterSpec oaepParams = new OAEPParameterSpec(
"SHA-256",
"MGF1",
new MGF1ParameterSpec("SHA-256"),
PSource.PSpecified.DEFAULT
);
cipher.init(Cipher.DECRYPT_MODE, privateKey, oaepParams);
}
byte[] ciphertextBytes = Base64.getDecoder().decode(encryptedData);
decryptedBytes = cipher.doFinal(ciphertextBytes);
} catch (NoSuchPaddingException | IllegalBlockSizeException | InvalidKeySpecException |
BadPaddingException | InvalidKeyException | NoSuchAlgorithmException |
InvalidAlgorithmParameterException e) {
throw new RuntimeException(e);
}
return new String(decryptedBytes, StandardCharsets.UTF_8);
}
public static EncryptedResponse encryptWithAES(String data, String publicKey, Boolean usePkcsPadding) {
try {
KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");
keyGenerator.init(256);
SecretKey secretKey = keyGenerator.generateKey();
byte[] iv = new byte[16];
new SecureRandom().nextBytes(iv);
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance(CIPHER);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);
byte[] cipherText = cipher.doFinal(data.getBytes());
String encryptedData = Base64.getEncoder().encodeToString(cipherText);
String encodedIV = Base64.getEncoder().encodeToString(iv);
String encodedSecretKey = Base64.getEncoder().encodeToString(secretKey.getEncoded());
String aesProperties = encodedSecretKey + "." + encodedIV;
String encryptedAesProperties = encryptUsingPublicKey(aesProperties, publicKey, usePkcsPadding);
return new EncryptedResponse(encryptedData, true, encryptedAesProperties);
} catch (InvalidAlgorithmParameterException | NoSuchPaddingException | IllegalBlockSizeException |
NoSuchAlgorithmException | BadPaddingException | InvalidKeyException e) {
throw new RuntimeException(e);
}
}
public static String decryptWithAES(String encryptedAESProperties, String encryptedData, String privateKey, Boolean usePkcsPadding) {
try {
String decodedAESProperties = EncryptionUtils.decryptWithPrivateKey(encryptedAESProperties, privateKey, usePkcsPadding);
int indexOfDelimiter = decodedAESProperties.indexOf('.');
String base64SecretKey = decodedAESProperties.substring(0, indexOfDelimiter);
String base64IV = decodedAESProperties.substring(indexOfDelimiter + 1);
byte[] decodedKey = Base64.getDecoder().decode(base64SecretKey);
SecretKey secretKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "AES");
byte[] decodedIV = Base64.getDecoder().decode(base64IV);
IvParameterSpec ivParameterSpec = new IvParameterSpec(decodedIV);
Cipher cipher = Cipher.getInstance(CIPHER);
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
byte[] cipherText = Base64.getDecoder().decode(encryptedData);
byte[] decryptedText = cipher.doFinal(cipherText);
return new String(decryptedText);
} catch (InvalidAlgorithmParameterException | NoSuchPaddingException | IllegalBlockSizeException |
NoSuchAlgorithmException | BadPaddingException | InvalidKeyException e) {
throw new RuntimeException(e);
}
}
}